home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2003 November A / PCWK1103A.iso / ABBYY FineReader 7.0 PRO / data1.cab / pdf_base.ps < prev    next >
Text File  |  2002-02-23  |  21KB  |  640 lines

  1. %    Copyright (C) 1994, 1996, 1997, 1998, 1999, 2000 Aladdin Enterprises.  All rights reserved.
  2. % This software is provided AS-IS with no warranty, either express or
  3. % implied.
  4. % This software is distributed under license and may not be copied,
  5. % modified or distributed except as expressly authorized under the terms
  6. % of the license contained in the file LICENSE in this distribution.
  7. % For more information about licensing, please refer to
  8. % http://www.ghostscript.com/licensing/. For information on
  9. % commercial licensing, go to http://www.artifex.com/licensing/ or
  10. % contact Artifex Software, Inc., 101 Lucas Valley Road #110,
  11. % San Rafael, CA  94903, U.S.A., +1(415)492-9861.
  12.  
  13. % $Id: pdf_base.ps,v 1.14.2.1 2002/02/22 19:45:55 ray Exp $
  14. % pdf_base.ps
  15. % Basic parser for PDF reader.
  16.  
  17. % This handles basic parsing of the file (including the trailer
  18. % and cross-reference table), as well as objects, object references,
  19. % streams, and name/number trees; it doesn't include any facilities for
  20. % making marks on the page.
  21.  
  22. /.setlanguagelevel where { pop 2 .setlanguagelevel } if
  23. .currentglobal true .setglobal
  24. /pdfdict where { pop } { /pdfdict 100 dict def } ifelse
  25. pdfdict begin
  26.  
  27. % Define the name interpretation dictionary for reading values.
  28. /valueopdict mark
  29.   (<<) cvn { mark } bind    % don't push an actual mark!
  30.   (>>) cvn /.dicttomark load
  31.   ([) cvn { mark } bind        % ditto
  32.   (]) cvn dup load
  33. %  /true true        % see .pdfexectoken below
  34. %  /false false        % ibid.
  35. %  /null null        % ibid.
  36.   /F dup cvx        % see Objects section below
  37.   /R dup cvx        % see Objects section below
  38.   /stream dup cvx    % see Streams section below
  39. .dicttomark readonly def
  40.  
  41. % ------ Utilities ------ %
  42.  
  43. % Define a scratch string.  The PDF language definition says that
  44. % no line in a PDF file can exceed 255 characters.
  45. /pdfstring 255 string def
  46.  
  47. % Read the previous line of a file.  If we aren't at a line boundary,
  48. % read the line containing the current position.
  49. % Skip any blank lines.
  50. /prevline        % - prevline <startpos> <substring>
  51.  { PDFfile fileposition dup () pdfstring
  52.    2 index 257 sub 0 .max PDFfile exch setfileposition
  53.     {        % Stack: initpos linepos line string
  54.       PDFfile fileposition
  55.       PDFfile 2 index readline pop
  56.       dup length 0 gt
  57.        { 3 2 roll 5 -2 roll pop pop 2 index }
  58.        { pop }
  59.       ifelse
  60.         % Stack: initpos linepos line string startpos
  61.       PDFfile fileposition 5 index ge { exit } if
  62.       pop
  63.     }
  64.    loop pop pop 3 -1 roll pop
  65.  } bind def
  66.  
  67. % Handle the PDF 1.2 #nn escape convention when reading from a file.
  68. % This should eventually be done in C.
  69. /.pdffixname {            % <execname> .pdffixname <execname'>
  70.   PDFversion 1.2 ge {
  71.     dup .namestring (#) search {
  72.       name#escape cvn exch pop
  73.     } {
  74.       pop
  75.     } ifelse
  76.   } if
  77. } bind def
  78. /name#escape            % <post> <(#)> <pre> name#escape <string>
  79. { exch pop
  80.   1 index 2 () /SubFileDecode filter dup (x) readhexstring
  81.         % Stack: post pre stream char t/f
  82.   not { /.pdftoken cvx /syntaxerror signalerror } if
  83.   exch closefile concatstrings
  84.   exch 2 1 index length 2 sub getinterval
  85.   (#) search { name#escape } if concatstrings
  86. } bind def
  87.  
  88. % Execute a file, interpreting its executable names in a given
  89. % dictionary.  The name procedures may do whatever they want
  90. % to the operand stack.
  91. /.pdftokenerror {        % <count> <opdict> <errtoken> .pdftokenerror -
  92.   BXlevel 0 le {
  93.     (%stderr) (w) file
  94.     dup (****************Unknown operator: ) writestring
  95.     dup 2 index .writecvs dup (\n) writestring flushfile
  96.   } if pop pop
  97.   count exch sub { pop } repeat    % pop all the operands
  98. } bind def
  99. /.pdfexectoken {        % <count> <opdict> <exectoken> .pdfexectoken ?
  100.   DEBUG { dup == flush } if
  101.   2 copy .knownget {
  102.     exch pop exch pop exch pop exec
  103.   } {
  104.         % Normally, true, false, and null would appear in opdict
  105.         % and be treated as "operators".  However, there is a
  106.         % special fast case in the PostScript interpreter for names
  107.         % that are defined in, and only in, systemdict and/or
  108.         % userdict: putting these three names in the PDF dictionaries
  109.         % destroys this property for them, slowing down their
  110.         % interpretation in all PostScript code.  Therefore, we
  111.         % check for them explicitly here instead.
  112.     dup dup dup /true eq exch /false eq or exch /null eq or {
  113.       exch pop exch pop //systemdict exch get
  114.     } {
  115.       .pdftokenerror
  116.     } ifelse
  117.   } ifelse
  118. } bind def
  119. /.pdfrun {            % <file> <opdict> .pdfrun -
  120.     % Construct a procedure with the stack depth, file and opdict
  121.     % bound into it.
  122.   1 index cvlit count 2 sub 3 1 roll mark mark 5 2 roll
  123.   {    % Stack: ..operands.. count opdict file
  124.     token {
  125.       dup type /nametype eq {
  126.     dup xcheck {
  127.       .pdfexectoken
  128.     } {
  129.       .pdffixname
  130.       exch pop exch pop DEBUG { dup ==only ( ) print flush } if
  131.     } ifelse
  132.       } {
  133.     exch pop exch pop DEBUG { dup ==only ( ) print flush } if
  134.       } ifelse
  135.     } {
  136.       (%%EOF) cvn cvx .pdfexectoken
  137.     } ifelse
  138.   }
  139.   aload pop .packtomark cvx
  140.   /loop cvx 2 packedarray cvx
  141.   { stopped /PDFsource } aload pop
  142.   PDFsource
  143.   { store { stop } if } aload pop .packtomark cvx
  144.   /PDFsource 3 -1 roll store exec
  145. } bind def
  146.  
  147. % Execute a file, like .pdfrun, for a marking context.
  148. % This temporarily rebinds LocalResources and DefaultMatrix.
  149. /.pdfruncontext {        % <resdict> <file> <opdict> .pdfruncontext -
  150.   /.pdfrun load LocalResources DefaultMatrix
  151.   /LocalResources 7 -1 roll store
  152.   /DefaultMatrix matrix currentmatrix store
  153.   3 .execn
  154.   /DefaultMatrix exch store
  155.   /LocalResources exch store
  156. } bind def
  157.  
  158. % Get the depth of the PDF operand stack.  The main program (pdf_main.ps)
  159. % sets pdfemptycount before calling .pdfrun.
  160. /.pdfcount {        % - .pdfcount <count>
  161.   count pdfemptycount sub
  162. } bind def
  163.  
  164. % ------ File reading ------ %
  165.  
  166. % Read the cross-reference entry for an (unresolved) object.
  167. % The caller must save and restore the PDFfile position if desired.
  168. % For invalid (free) objects, we return 0.
  169. /readxrefentry        % <object#> readxrefentry <objpos>
  170.  { dup Objects exch lget
  171.    PDFfile exch setfileposition
  172.    PDFfile token pop        % object position
  173.    PDFfile token pop        % generation #
  174.    PDFfile token pop        % n or f
  175.    dup /n eq
  176.     { pop 1 add dup 255 gt
  177.        { Generations ltype /stringtype eq
  178.       {        % Convert Generations from a string to an array.
  179.         larray Generations llength lgrowto dup
  180.         0 1 2 index llength 1 sub
  181.          { Generations 1 index lget lput dup
  182.          }
  183.         for pop /Generations exch store
  184.       }
  185.      if
  186.        }
  187.       if
  188.     }
  189.     { /f eq
  190.        { pop 0 }
  191.        { /readxrefentry cvx /syntaxerror signalerror }
  192.       ifelse
  193.     }
  194.    ifelse
  195.         % Stack: obj# objpos 1+gen#
  196.    Generations 4 -1 roll 3 -1 roll lput
  197.  } bind def
  198.  
  199. % ================================ Objects ================================ %
  200.  
  201. % Since we may have more than 64K objects, we have to use a 2-D array to
  202. % hold them (and the parallel Generations structure).
  203. /lshift 9 def
  204. /lnshift lshift neg def
  205. /lsubmask 1 lshift bitshift 1 sub def
  206. /lsublen lsubmask 1 add def
  207. /larray {    % - larray <larray>
  208.   [ [] ]
  209. } bind def
  210. /lstring {    % - lstring <lstring>
  211.   [ () ]
  212. } bind def
  213. /ltype {    % <lseq> type <type>
  214.   0 get type
  215. } bind def
  216. /lget {        % <lseq> <index> lget <value>
  217.   dup //lsubmask and 3 1 roll //lnshift bitshift get exch get
  218. } bind def
  219. /lput {        % <lseq> <index> <value> lput -
  220.   3 1 roll
  221.   dup //lsubmask and 4 1 roll //lnshift bitshift get
  222.   3 1 roll put
  223. } bind def
  224. /llength {    % <lseq> llength <length>
  225.   dup length 1 sub dup //lshift bitshift
  226.   3 1 roll get length add
  227. } bind def
  228. % lgrowto assumes newlength > llength(lseq)
  229. /growto {    % <string/array> <length> growto <string'/array'>
  230.   1 index type /stringtype eq { string } { array } ifelse
  231.   2 copy copy pop exch pop
  232. } bind def
  233. /lgrowto {    % <lseq> <newlength> lgrowto <lseq'>
  234.     dup //lsubmask add //lnshift bitshift dup 3 index length gt {
  235.     % Add more sub-arrays.  Start by completing the last existing one.
  236.         % Stack: lseq newlen newtoplen
  237.     3 -1 roll dup llength 1 sub //lsubmask or 1 add lgrowto
  238.         % Stack: newlen newtoplen lseq
  239.     [ exch aload pop
  240.     counttomark 2 add -1 roll        % newtoplen
  241.     counttomark sub { dup 0 0 getinterval lsublen growto } repeat
  242.     dup 0 0 getinterval ] exch
  243.   } {
  244.     pop
  245.   } ifelse
  246.     % Expand the last sub-array.
  247.   1 sub //lsubmask and 1 add
  248.   exch dup dup length 1 sub 2 copy
  249.         % Stack: newsublen lseq lseq len-1 lseq len-1
  250.   get 5 -1 roll growto put
  251. } bind def
  252. /lforall {    % <lseq> <proc> lforall -
  253.   /forall cvx 2 packedarray cvx forall
  254. } bind def
  255.  
  256. % We keep track of PDF objects using the following PostScript variables:
  257. %
  258. %    Generations (lstring): Generations[N] holds 1+ the current
  259. %        generation number for object number N.  (As far as we can tell,
  260. %        this is needed only for error checking.)  For free objects,
  261. %        Generations[N] is 0.
  262. %
  263. %    Objects (larray): If object N is loaded, Objects[N] is the actual
  264. %        object; otherwise, Objects[N] is an executable integer giving
  265. %        the file offset of the object's entry in the cross-reference
  266. %        table.
  267. %
  268. %    GlobalObjects (dictionary): If object N has been resolved in
  269. %        global VM, GlobalObjects[N] is the same as Objects[N]
  270. %        (except that GlobalObjects itself is stored in global VM,
  271. %        so the entry will not be deleted at the end of the page).
  272. %
  273. %    IsGlobal (lstring): IsGlobal[N] = 1 iff object N was resolved in
  274. %        global VM.  This is an accelerator to avoid having to do a
  275. %        dictionary lookup in GlobalObjects when resolving every object.
  276.  
  277. % Initialize the PDF object tables.
  278. /initPDFobjects {        % - initPDFobjects -
  279.   /Objects larray def
  280.   /Generations lstring def
  281.   .currentglobal true .setglobal
  282.   /GlobalObjects 20 dict def
  283.   .setglobal
  284.   /IsGlobal lstring def
  285. } bind def
  286.  
  287. % Grow the tables to a specified size.
  288. /growPDFobjects {        % <minsize> growPDFobjects -
  289.   dup Objects llength gt {
  290.     dup Objects exch lgrowto /Objects exch def
  291.   } if
  292.   dup Generations llength gt {
  293.     dup Generations exch lgrowto /Generations exch def
  294.   } if
  295.   dup IsGlobal llength gt {
  296.     dup IsGlobal exch lgrowto /IsGlobal exch def
  297.   } if
  298.   pop
  299. } bind def
  300.  
  301. % We represent an unresolved object reference by a procedure of the form
  302. % {obj# gen# resolveR}.  This is not a possible PDF object, because PDF has
  303. % no way to represent procedures.  Since PDF in fact has no way to represent
  304. % any PostScript object that doesn't evaluate to itself, we can 'force'
  305. % a possibly indirect object painlessly with 'exec'.
  306. % Note that since we represent streams by executable dictionaries
  307. % (see below), we need both an xcheck and a type check to determine
  308. % whether an object has been resolved.
  309. /resolved? {        % <object#> resolved? <value> true
  310.             % <object#> resolved? false
  311.   Objects 1 index lget dup xcheck {
  312.     dup type /integertype eq {
  313.         % Check whether the object is in GlobalObjects.
  314.       pop IsGlobal 1 index lget 0 eq {
  315.     pop false
  316.       } {
  317.         % Update Objects from GlobalObjects
  318.     DEBUG { (%Global=>local: ) print dup == } if
  319.     GlobalObjects 1 index get dup Objects 4 1 roll lput true
  320.       } ifelse
  321.     } {
  322.       exch pop true
  323.     } ifelse
  324.   } {
  325.     exch pop true
  326.   } ifelse
  327. } bind def
  328. /oforce /exec load def
  329. /oget {        % <array> <index> oget <object>
  330.         % <dict> <key> oget <object>
  331.         % Before release 6.20, this procedure stored the resolved
  332.         % object back into the referring slot.  In order to support
  333.         % PDF linearization, we no longer do this.
  334.   get oforce
  335. } bind def
  336. % A null value in a dictionary is equivalent to an omitted key;
  337. % we must check for this specially.
  338. /knownoget {    % <dict> <key> knownoget <value> true
  339.         % <dict> <key> knownoget false
  340.         % See oget above regarding this procedure.
  341.   .knownget {
  342.     oforce dup null eq { pop false } { true } ifelse
  343.   } {
  344.     false
  345.   } ifelse
  346. } bind def
  347.  
  348. % PDF 1.1 defines a 'foreign file reference', but not its meaning.
  349. % Per the specification, we convert these to nulls.
  350. /F {        % <file#> <object#> <generation#> F <object>
  351.         % Some PDF 1.1 files use F as a synonym for f!
  352.    .pdfcount 3 lt { f } { pop pop pop null } ifelse
  353. } bind def
  354.  
  355. /checkgeneration {  % <object#> <generation#> checkgeneration <object#> <OK>
  356.   Generations 2 index lget 1 sub 1 index eq {
  357.     pop true
  358.   } {
  359.     QUIET not {
  360.       Generations 2 index lget 0 eq {
  361.     (Warning: reference to free object: )
  362.       } {
  363.     (Warning: wrong generation: )
  364.       } ifelse print 1 index =only ( ) print =only ( R) =
  365.     } {
  366.       pop
  367.     } ifelse false
  368.   } ifelse
  369. } bind def
  370. /R {        % <object#> <generation#> R <object>
  371.   /resolveR cvx 3 packedarray cvx
  372. } bind def
  373.  
  374. % If we encounter an object definition while reading sequentially,
  375. % we just store it away and keep going.
  376. /objopdict mark
  377.   valueopdict { } forall
  378.   /endobj dup cvx
  379. .dicttomark readonly def
  380. /obj {            % <object#> <generation#> obj <object>
  381.   PDFfile objopdict .pdfrun
  382. } bind def
  383. /endobj {        % <object#> <generation#> <object> endobj <object>
  384.   3 1 roll
  385.         % Read the xref entry if we haven't yet done so.
  386.         % This is only needed for generation # checking.
  387.   1 index resolved? {
  388.     pop
  389.   } {
  390.     PDFfile fileposition
  391.     2 index readxrefentry pop
  392.     PDFoffset add PDFfile exch setfileposition
  393.   } ifelse
  394.   checkgeneration {
  395.         % The only global objects we bother to save are
  396.         % (resource) dictionaries.
  397.     1 index dup gcheck exch type /dicttype eq and {
  398.       DEBUG { (%Local=>global: ) print dup == } if
  399.       GlobalObjects 1 index 3 index put
  400.       IsGlobal 1 index 1 put
  401.     } if
  402.     Objects exch 2 index lput
  403.   } {
  404.     pop pop null
  405.   } ifelse
  406. } bind def
  407.  
  408. % When resolving an object reference, we stop at the endobj.
  409. /resolveopdict mark
  410.   valueopdict { } forall
  411.   /endobj { endobj exit } bind
  412.                 % OmniForm generates PDF file with endobj missing in some
  413.                 % objects. AR ignores this. So we have to do it too.
  414.   /obj { pop pop endobj exit } bind
  415. .dicttomark readonly def
  416. /resolveR {        % <object#> <generation#> resolveR <object>
  417.   DEBUG { (%Resolving: ) print 2 copy 2 array astore == } if
  418.   1 index resolved? {
  419.     exch pop exch pop
  420.   } {
  421.     PDFfile fileposition 3 1 roll
  422.     1 index readxrefentry
  423.     3 1 roll checkgeneration {
  424.             % Stack: savepos objpos obj#
  425.      exch PDFoffset add PDFfile exch setfileposition
  426.      PDFfile token pop 2 copy ne
  427.       { (xref error!) = /resolveR cvx /rangecheck signalerror
  428.       }
  429.      if pop PDFfile token pop
  430.      PDFfile token pop /obj ne
  431.       { (xref error!) = /resolveR cvx /rangecheck signalerror
  432.       }
  433.      if
  434.      pdf_run_resolve    % PDFfile resolveopdict .pdfrun
  435.     }
  436.     {        % Don't cache if the generation # is wrong.
  437.      pop pop null
  438.     } ifelse
  439.     exch PDFfile exch setfileposition
  440.   } ifelse
  441. } bind def      
  442.  
  443. % ================================ Streams ================================ %
  444.  
  445. % We represent a stream by an executable dictionary that contains,
  446. % in addition to the contents of the original stream dictionary:
  447. %    /File - the file or string where the stream contents are stored,
  448. %      if the stream is not an external one.
  449. %    /FilePosition - iff File is a file, the position in the file
  450. %      where the contents start.
  451. %    /StreamKey - the key used to decrypt this stream, if any.
  452. % We do the real work of constructing the data stream only when the
  453. % contents are needed.
  454.  
  455. % Construct a stream.  The length is not reliable in the face of
  456. % different end-of-line conventions, but it's all we've got.
  457. %
  458. % PDF files are inconsistent about what may fall between the 'stream' keyword
  459. % and the actual stream data, and it appears that no one algorithm can
  460. % detect this reliably.  We used to try to guess whether the file included
  461. % extraneous \r and/or \n characters, but we no longer attempt to do so,
  462. % especially since the PDF 1.2 specification states flatly that the only
  463. % legal terminators following the 'stream' keyword are \n or \r\n, both of
  464. % which are properly skipped and discarded by the token operator.
  465. /stream {    % <dict> stream <modified_dict>
  466.   dup /F known dup PDFsource PDFfile eq or {
  467.     not {
  468.       dup /File PDFfile put
  469.       dup /FilePosition PDFfile fileposition put
  470.       DEBUG { (%FilePosition: ) print dup /FilePosition get == } if
  471.     } if
  472.     PDFfile fileposition 1 index /Length oget add
  473.       PDFfile exch setfileposition
  474.   } {
  475.     pop
  476.     % We're already reading from a stream, which we can't reposition.
  477.     % Capture the sub-stream contents in a string.
  478.     dup /Length oget string PDFsource exch readstring
  479.     not {
  480.       (Unexpected EOF in stream!) =
  481.       /stream cvx /rangecheck signalerror
  482.     } if
  483.     1 index exch /File exch put
  484.   } ifelse
  485.   PDFsource token pop
  486.     /endstream ne { /stream cvx /syntaxerror signalerror } if
  487.   cvx
  488. } bind def
  489. /endstream {
  490.   exit
  491. } bind def
  492.  
  493. % Contrary to the published PDF (1.3) specification, Acrobat Reader
  494. % accepts abbreviated filter names everywhere, not just for in-line images,
  495. % and some applications (notably htmldoc) rely on this.
  496. /unabbrevfilterdict mark
  497.   /AHx /ASCIIHexDecode  /A85 /ASCII85Decode  /CCF /CCITTFaxDecode
  498.   /DCT /DCTDecode  /Fl /FlateDecode  /LZW /LZWDecode  /RL /RunLengthDecode
  499. .dicttomark readonly def
  500.  
  501. % Extract and apply filters.
  502. /filterparms {        % <dict> <DPkey> <Fkey> filterparms
  503.             %   <dict> <parms> <filternames>
  504.   2 index exch .knownget {
  505.     exch 2 index exch .knownget {
  506.         % Both filters and parameters.
  507.       exch dup type /nametype eq {
  508.     1 array astore exch 1 array astore exch
  509.       } if
  510.     } {
  511.         % Filters, but no parameters.
  512.       null exch
  513.       dup type /nametype eq { 1 array astore } if
  514.     } ifelse
  515.   } {
  516.         % No filters: ignore parameters, if any.
  517.     pop null { }
  518.   } ifelse
  519. } bind def
  520. /filtername {        % <filtername> filtername <filtername'>
  521.   //unabbrevfilterdict 1 index .knownget { exch pop } if
  522. } bind def
  523. /applyfilters {        % <parms> <source> <filternames> applyfilters <stream>
  524.   2 index null eq {
  525.     { filtername filter }
  526.   } {
  527.     {        % Stack: parms stream filtername
  528.       2 index 0 oget dup null eq { pop } { exch } ifelse filtername filter
  529.       exch dup length 1 sub 1 exch getinterval exch
  530.     }
  531.   } ifelse forall exch pop
  532. } bind def
  533.  
  534. % Resolve a stream dictionary to a PostScript stream.
  535. % Streams with no filters require special handling:
  536. %    - If we are going to interpret their contents, we let endstream
  537. %      terminate the interpretation loop;
  538. %    - If we are just going to read data from them, we impose
  539. %      a SubFileDecode filter that reads just the requisite amount of data.
  540. % Note that, in general, resolving a stream repositions PDFfile.
  541. % Clients must save and restore the position of PDFfile themselves.
  542. /resolvestream {    % <streamdict> <readdata?> resolvestream <stream>
  543.   1 index /F .knownget {
  544.         % This stream is stored on an external file.
  545.     (r) file 3 -1 roll
  546.     /FDecodeParms /FFilter filterparms
  547.         % Stack: readdata? file dict parms filternames
  548.     4 -1 roll exch
  549.     pdf_decrypt_stream
  550.     applyfilters
  551.   } {
  552.     exch dup /FilePosition .knownget {
  553.       1 index /File get exch setfileposition
  554.     } if
  555.         % Stack: readdata? dict
  556.     /DecodeParms /Filter filterparms
  557.         % Stack: readdata? dict parms filternames
  558.     2 index /File get exch
  559.         % Stack: readdata? dict parms file/string filternames
  560.     pdf_decrypt_stream        % add decryption if needed
  561.     dup length 0 eq {
  562.         % All the PDF filters have EOD markers, but in this case
  563.         % there is no specified filter.
  564.       pop exch pop
  565.         % Stack: readdata? dict file/string
  566.       2 index {
  567.         % We're going to read data; use a SubFileDecode filter.
  568.     1 index /Length oget () /SubFileDecode filter
  569.       } {
  570.     dup type /filetype ne {
  571.         % Use a SubFileDecode filter to read from a string.
  572.       0 () /SubFileDecode filter
  573.     } if
  574.       } ifelse
  575.     } {
  576.       applyfilters
  577.     } ifelse
  578.   } ifelse
  579.         % Stack: readdata? dict file
  580.   exch pop exch pop
  581. } bind def
  582.  
  583. % ============================ Name/number trees ============================ %
  584.  
  585. /nameoget {        % <nametree> <key> nameoget <obj|null>
  586.   exch /Names exch .treeget
  587. } bind def
  588.  
  589. /numoget {        % <numtree> <key> numoget <obj|null>
  590.   exch /Nums exch .treeget
  591. } bind def
  592.  
  593. /.treeget {        % <key> <leafkey> <tree> .treeget <obj|null>
  594.   dup /Kids knownoget {
  595.     exch pop .branchget
  596.   } {
  597.     exch get .leafget
  598.   } ifelse
  599. } bind def
  600.  
  601. /.branchget {        %  <key> <leafkey> <kids> .branchget <obj|null>
  602.   dup length 0 eq {
  603.     pop pop pop null
  604.   } {
  605.     dup length -1 bitshift 2 copy oget
  606.             % Stack: key leafkey kids mid kids[mid]
  607.     dup /Limits oget aload pop
  608.             % Stack: key leafkey kids mid kids[mid] min max
  609.     6 index lt {
  610.       pop pop
  611.       1 add 1 index length 1 index sub getinterval .branchget
  612.     } {
  613.       5 index gt {
  614.     pop
  615.     0 exch getinterval .branchget
  616.       } {
  617.     exch pop exch pop .treeget
  618.       } ifelse
  619.     } ifelse
  620.   } ifelse
  621. } bind def
  622.  
  623. /.leafget {        % <key> <pairs> .leafget <obj|null>
  624.   dup length 2 eq {
  625.     dup 0 get 2 index eq { 1 oget } { pop null } ifelse
  626.     exch pop
  627.   } {
  628.     dup length -1 bitshift -2 and 2 copy oget
  629.             % Stack: key pairs mid pairs[mid]
  630.     3 index gt { 0 exch } { 1 index length 1 index sub } ifelse
  631.     getinterval .leafget
  632.   } ifelse
  633. } bind def
  634.  
  635. end            % pdfdict
  636. .setglobal
  637.